/*
 * This file is part of Plop Messenger.
 *
 * Plop Messenger is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  Plop Messenger is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Plop Messenger.  If not, see <http://www.gnu.org/licenses/>.
 */
package pm;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import pm.net.PosteMsg;
import pm.net.PosteMsgListener;
import pm.util.Msg;
import pm.util.Options;

public class ChanManager implements ChanListener, PosteMsgListener {

	public static final String SYSTEM_CHAN = "SYSTEM";
	private ChanFactory chanFactory;
	private Map<String, Chan> chans = new HashMap<String, Chan>();
	private String nick;
	private Options options;
	private PosteMsg posteMsg;

	public ChanManager(ChanFactory chanFactory, PosteMsg posteMsg,
			Options options) {
		this.chanFactory = chanFactory;
		this.posteMsg = posteMsg;
		this.options = options;
		nick = options.getString("nickname", Msg.get("pm.defaultnickname"));
		posteMsg.addListener(this);
		Chan systemChan = chanFactory.createChan(Msg.get("pm.chan.system"));
		systemChan.addListener(this);
		chans.put(SYSTEM_CHAN, systemChan);
		systemChan.writeln(Msg.get("pm.welcome", nick));
		ArrayList<String> chan = options.getObject("autojoin.chans",
				new ArrayList<String>());
		for (String c : chan) {
			Chan ch = chanFactory.createChan(c);
			chans.put(c, ch);
			ch.addListener(this);
			posteMsg.connect(c, nick, Constantes.DEFAULT_PASSWORD);
		}
	}

	private void broadcastMessage(Message m) {
		for (String c : chans.keySet()) {
			if (!c.equals(SYSTEM_CHAN)) {
				m.setTo(c);
				posteMsg.sendMessage(m);
			}
		}
	}

	private void commandFrom(Chan chan, String command, String... params) {
		if (command.equals("join")) {

			if (params.length < 1 || params.length > 2) {
				chan.writeln("% /join chan [password]");
				return;
			}
			String ch = params[0];
			if (!chans.containsKey(ch)) {
				Chan c = chanFactory.createChan(ch);
				chans.put(ch, c);
				c.addListener(this);
				posteMsg.connect(ch, nick, (params.length == 2) ? params[1]
						: Constantes.DEFAULT_PASSWORD);
			} else {
				chan.writeln(Msg.get("pm.me.alreadyjoin", ch));
			}
		} else if (command.equals("quit")) {
			if (!getChanName(chan).equals(SYSTEM_CHAN)) {
				chanFactory.removeChan(chan);
				chan.removeListener(this);
				String ch = getChanName(chan);
				posteMsg.disconnect(ch, nick);
				chans.remove(getChanName(chan));
			} else {
				chan.writeln(Msg.get("pm.error.quitsystem"));
			}
		} else if (command.equals("nick")) {

			if (params.length == 1) {
				String n = params[0];
				if (!n.contains(":") && !n.startsWith("#") && n.length() > 0) {
					Message m = new Message("", n, nick, Message.NICK_CHANGED);
					nick = n;
					options.setString("nickname", nick);
					broadcastMessage(m);
				}
			}
		} else if (command.equals("help")) {
			chan.writeln(Msg.get("pm.command.help"));
		} else if (command.equals("autojoin")) {
			ArrayList<String> chans = options.getObject("autojoin.chans",
					new ArrayList<String>());
			if (params.length == 1) {
				String c = params[0];
				if (c.equalsIgnoreCase("reset")) {
					chans.clear();
				} else if (c.equalsIgnoreCase("list")) {
					chan.writeln(Msg.get("pm.autojoin.list.0"));
					for (String s : chans) {
						chan.writeln(Msg.get("pm.autojoin.list.1", s));
					}
				}
			} else if (params.length == 2) {
				String c = params[0];
				String ch = params[1];
				if (c.equalsIgnoreCase("add")) {
					if (!chans.contains(ch))
						chans.add(ch);
				} else if (c.equalsIgnoreCase("del")) {
					chans.remove(ch);
				}
			}
			options.setObject("autojoin.chans", chans);
		} else if (command.equals("dateformat")) {
			if (params.length == 1)
				options.setString("dateformat", params[0]);
		} else if (command.equals("showcrypted")) {
			if (params.length == 1) {
				options.setBool("showcrypted", Boolean.parseBoolean(params[0]));
			}
		} else {
			chan.writeln(Msg.get("pm.command.unknow", command));
		}
	}

	private String getChanName(Chan chan) {
		for (String s : chans.keySet()) {
			if (chans.get(s) == chan) {
				return s;
			}
		}
		return null;
	}

	private void messageFrom(Chan chan, String message) {
		String cha = getChanName(chan);
		if (!cha.equals(SYSTEM_CHAN)) {
			Message m = new Message(nick, cha, message);
			posteMsg.sendMessage(m);
		}
	}

	@Override
	public void messageReceived(PosteMsg posteMsg, Message message) {
		SimpleDateFormat format = new SimpleDateFormat(options.getString(
				"dateformat", "kk:mm"));
		String date = format.format(new Date());
		if (posteMsg == this.posteMsg) {
			String msg = Msg.get("pm.error.badtype");
			switch (message.getType()) {
			case Message.CLIENT_CONNECTED:
				msg = Msg.get("pm.client.connected", message.getFrom(), message
						.getTo(), date);
				break;
			case Message.CLIENT_EXITED:
				msg = Msg.get("pm.client.disconnected", message.getFrom(),
						message.getTo(), date);
				break;
			case Message.NICK_CHANGED:
				msg = Msg.get("pm.client.nickchanged", message.getMessage(),
						message.getFrom(), date);
				break;
			case Message.MESSAGE:
				msg = Msg.get("pm.client.message", message.getFrom(), message
						.getMessage(), date);
				break;
			case Message.CRYPTED:
				msg = Msg.get("pm.client.message", Msg.get("pm.client.unknow"),
						message.getMessage(), date);
				break;
			}

			if (options.getBool("showcrypted", false)
					|| message.getType() != Message.CRYPTED) {
				Chan c = chans.get(message.getTo());
				c.writeln(msg);
			}
		}
	}

	@Override
	public void newCommand(Chan chan, String ligne) {
		if (chans.containsValue(chan)) {
			ligne = ligne.trim();
			if (!ligne.startsWith("/")) {
				messageFrom(chan, ligne);
			} else {
				String command = null;
				ArrayList<String> params = new ArrayList<String>();
				String tmpString = "";
				boolean force = false, guill = false;
				for (int i = 1; i < ligne.length(); i++) {
					char c = ligne.charAt(i);
					if (!force) {
						if (c == '\\') {
							force = true;
						} else if (c == '"') {
							guill = !guill;
						} else if (c == ' ' && !guill) {
							if (command == null) {
								command = tmpString;
							} else if (tmpString.length() > 0) {
								params.add(tmpString);
							}
							tmpString = "";
						} else {
							tmpString += c;
						}
					} else {
						tmpString += c;
						force = false;
					}
				}
				if (command == null) {
					command = tmpString;
				} else if (tmpString.length() > 0) {
					params.add(tmpString);
				}
				commandFrom(chan, command, params.toArray(new String[params
						.size()]));
			}
		}
	}

}
